<template>
  <view class="container">
    <CustomNavbar
    />

    <!-- 全局加载遮罩（在数据载入完成前显示） -->
    <view v-if="!isLoaded" class="loading-overlay">
      <view class="loading-box">
        <view v-if="longLoading" class="loading-note">当前参赛人员较多，请耐心等待</view>
        <view class="spinner">
          <view class="dot" v-for="n in 3" :key="n"></view>
        </view>
        <view class="loading-text">数据加载中...</view>
      </view>
    </view>

    <PublicContentLayout>
      <view class="banner-area">
        <view class="btn" @tap="navigateToPartnerRank">
          查看热力团队排行榜
        </view>
      </view>

      <scroll-view class="region-scroll" scroll-x="true" :scroll-left="regionScrollLeft" scroll-with-animation="true">
        <view class="region-tab-inner-wrapper">
          <view
            v-for="(tab, idx) in regionTabs"
            :key="idx"
            :id="'region-' + idx"
            class="region-tab"
            :class="{ active: activeRegionIndex === idx }"
            @tap="onRegionClick(idx)"
          >
            {{ tab }}
          </view>
        </view>
      </scroll-view>

      <view class="top-three-box">
        <!-- 左：第2名 -->
        <view class="top-item rank-two">
          <image v-if="(topPartnerList && topPartnerList[1] && topPartnerList[1].isTieGroup)"  class="avatar" :src="'/static/default-avatar.jpg'" @tap="openTieModal((topPartnerList && topPartnerList[1]) ? topPartnerList[1].totalScore : null, 2)"></image>
          <UserAvatar v-else :avatar-image="(topPartnerList && topPartnerList[1]) ? (topPartnerList[1].avatarImage || '/static/default-avatar.jpg') : '/static/default-avatar.jpg'" :title="(topPartnerList && topPartnerList[1] && topPartnerList[1].title) || ''" size="medium" @tap="topPartnerList && topPartnerList[1] && !topPartnerList[1].isTieGroup && navigateToPersonal(topPartnerList[1].workNumber)" />
          <text class="name">
            <template v-if="(topPartnerList && topPartnerList[1] && topPartnerList[1].isTieGroup)">多名并列</template>
            <template v-else>{{ (topPartnerList && topPartnerList[1]) ? topPartnerList[1].name : '--' }}</template>
          </text>
          <text class="score">总分: {{ (topPartnerList && topPartnerList[1]) ? topPartnerList[1].totalScore : '--' }}</text>
        </view>
        <!-- 中：第1名 -->
        <view class="top-item rank-one">
          <image v-if="(topPartnerList && topPartnerList[0] && topPartnerList[0].isTieGroup)"  class="avatar" :src="'/static/default-avatar.jpg'" @tap="openTieModal((topPartnerList && topPartnerList[0]) ? topPartnerList[0].totalScore : null, 1)"></image>
          <UserAvatar v-else :avatar-image="(topPartnerList && topPartnerList[0]) ? (topPartnerList[0].avatarImage || '/static/default-avatar.jpg') : '/static/default-avatar.jpg'" :title="(topPartnerList && topPartnerList[0] && topPartnerList[0].title) || ''" size="medium" @tap="topPartnerList && topPartnerList[0] && !topPartnerList[0].isTieGroup && navigateToPersonal(topPartnerList[0].workNumber)" />
          <text class="name">
            <template v-if="(topPartnerList && topPartnerList[0] && topPartnerList[0].isTieGroup)">多名并列</template>
            <template v-else>{{ (topPartnerList && topPartnerList[0]) ? topPartnerList[0].name : '--' }}</template>
          </text>
          <text class="score">总分: {{ (topPartnerList && topPartnerList[0]) ? topPartnerList[0].totalScore : '--' }}</text>
        </view>
        <!-- 右：第3名 -->
        <view class="top-item rank-three">
          <image v-if="(topPartnerList && topPartnerList[2] && topPartnerList[2].isTieGroup)"  class="avatar" :src="'/static/default-avatar.jpg'" @tap="openTieModal((topPartnerList && topPartnerList[2]) ? topPartnerList[2].totalScore : null, 3)"></image>
          <UserAvatar v-else :avatar-image="(topPartnerList && topPartnerList[2]) ? (topPartnerList[2].avatarImage || '/static/default-avatar.jpg') : '/static/default-avatar.jpg'" :title="(topPartnerList && topPartnerList[2] && topPartnerList[2].title) || ''" size="medium" @tap="topPartnerList && topPartnerList[2] && !topPartnerList[2].isTieGroup && navigateToPersonal(topPartnerList[2].workNumber)" />
          <text class="name">
            <template v-if="(topPartnerList && topPartnerList[2] && topPartnerList[2].isTieGroup)">多名并列</template>
            <template v-else>{{ (topPartnerList && topPartnerList[2]) ? topPartnerList[2].name : '--' }}</template>
          </text>
          <text class="score">总分: {{ (topPartnerList && topPartnerList[2]) ? topPartnerList[2].totalScore : '--' }}</text>
        </view>
      </view>

      <!-- 并列弹窗 -->
      <view v-if="showTieModal" class="modal-overlay">
        <view class="modal-box">
          <view class="modal-header">
            <text>{{ tieTitle }}</text>
            <view class="close-btn" @tap="closeTieModal">关闭</view>
          </view>
          <scroll-view class="modal-list" scroll-y="true">
            <view class="team-list-item" v-for="(item, idx) in tieModalList" :key="idx" @tap="navigateToPersonal(item.workNumber)">
              <UserAvatar :avatar-image="item.avatarImage || '/static/default-avatar.jpg'" :title="item.title" size="small" />
              <text class="partner-name">{{ item.name }}</text>
              <text class="score-value">{{ item.totalScore }}</text>
            </view>
          </scroll-view>
        </view>
      </view>

      <view class="team-list-box">
        <scroll-view scroll-y="true" class="list-scroll-area">
          <view class="team-list-item" v-for="(item, index) in groupedPartnerList" :key="index" @tap="navigateToPersonal(item.workNumber)">
            <view class="rank-column">
              <text class="rank-text">{{ item.rank }}</text>
            </view>
            <UserAvatar :avatar-image="item.avatarImage || '/static/default-avatar.jpg'" :title="(item && item.title) || ''" size="small" />
            <text class="partner-name">
              <template>{{ item.name }}</template>
            </text>
            <text class="score-value">{{ item.totalScore }}</text>
          </view>
        </scroll-view>
        <view class="my-partner-info" v-if="myPartnerInfo.rank !== '-'">
          <view class="my-partner-info-inner">
            <view class="my-rank-column">
              <text class="rank-text">{{ myPartnerInfo.rank }}</text>
            </view>
            <UserAvatar :avatar-image="myPartnerInfo.avatarImage || '/static/default-avatar.jpg'" :title="(myPartnerInfo && myPartnerInfo.title) || ''" size="small" />
            <text class="partner-name">{{ myPartnerInfo.name }}</text>
            <text class="score-value">{{ myPartnerInfo.totalScore }}</text>
          </view>
        </view>
      </view>
    </PublicContentLayout>
  </view>
</template>

<script>
import CustomNavbar from '@/components/CustomNavbar.vue'
import PublicContentLayout from '@/components/PublicContentLayout.vue'
import UserAvatar from '@/components/UserAvatar.vue'
import createLiteTable from '@/utils/tableLite.js'
import { searchTeamAnswerInfo } from '@/api/base.js'
import AllUserInfo from '../../static/AllUserInfo'

export default {
  components: { CustomNavbar, PublicContentLayout, UserAvatar },
  data() {
    return {
      // 区域标签条数据
      regionTabs: ['全国'],
      activeRegionIndex: 0,
      regionScrollLeft: 0,
      topPartnerList: [],
      partnerRankList: [],
      groupedPartnerList: [],
      tableInfo: [],
      currentUserWorkNumber: '',
      myPartnerInfo: {
        rank: '-',
        name: '--',
        totalScore: 0,
        myContribution: 0
      },
      showTieModal: false,
      tieTitle: '',
      tieModalList: [],
      isShowLookBox: false, // 是否是参赛人员
      isLoaded: false,
      longLoading: false, // 是否处于长加载状态
      longLoadingTimer: null,
      renderStabilityTimer: null,
    }
  },
  async onLoad() {
    await this.getCurrentUserWorkNumber();
    await this.getTableInfo();
    dd.getStorage({
      key: 'isShowLookBox',
      success: (res) => {
        this.isShowLookBox = res.data;
      },
    });
    this.startLongLoadingTimer(); // 开始长加载计时器
  },
  onReady() {
    // 修复Android渲染问题
    setTimeout(() => { dd.setNavigationBar({ title: '' }); }, 100);
    const menu = dd.getMenuButtonBoundingClientRect && dd.getMenuButtonBoundingClientRect();
    if (menu) {
      console.log(menu, "胶囊按钮位置");
    } else {
      console.log("dd.getMenuButtonBoundingClientRect 不可用，跳过胶囊按钮位置检测");
    }
    // 初始化时将默认激活的tab居中
    this.$nextTick(() => {
      this.centerActiveRegion();
    });
  },
  methods: {
    /**
     * Persist split TableInfo into storage when large.
     * If list.length > chunkSize (500) it will write TableInfo1..N and set TableInfo_count.
     * Returns a Promise that resolves once writes complete.
     */
    saveTableInfo(list) {
      return new Promise((resolve) => {
        try {
          const chunkSize = 300;
          const chunks = [];
          const arr = Array.isArray(list) ? list : (Array.isArray(list.data) ? list.data : []);
          for (let i = 0; i < arr.length; i += chunkSize) {
            chunks.push(arr.slice(i, i + chunkSize));
          }
          if (chunks.length === 0) chunks.push([]);
          const newCount = chunks.length;

          try {
            dd.getStorage({
              key: 'TableInfo_count',
              success: (res) => {
                const oldCount = Number(res && res.data) || 0;
                if (oldCount > newCount) {
                  for (let k = newCount + 1; k <= oldCount; k++) {
                    try { dd.removeStorage({ key: 'TableInfo' + k }); } catch (e) {}
                  }
                }
              },
              fail: () => {},
            });
          } catch (e) {}

          const setPromises = chunks.map((chunk, idx) => new Promise((resSet) => {
            try {
              dd.setStorage({ key: 'TableInfo' + (idx + 1), data: createLiteTable(chunk), success: () => resSet(), fail: () => resSet() });
            } catch (e) { resSet(); }
          }));

          Promise.all(setPromises).then(() => {
            try { dd.setStorage({ key: 'TableInfo_count', data: newCount, success: () => resolve(), fail: () => resolve() }); } catch (e) { resolve(); }
          }).catch(() => { try { dd.setStorage({ key: 'TableInfo_count', data: newCount, success: () => resolve(), fail: () => resolve() }); } catch (e) { resolve(); } });
        } catch (e) { resolve(); }
      });
    },

    /**
     * Load TableInfo by reading TableInfo_count and then TableInfo1..N and concatenating.
     * Falls back to legacy single 'TableInfo' key if TableInfo_count not present.
     * Resolves to an array (possibly empty).
     */
    loadTableInfo() {
      return new Promise((resolve) => {
        try {
          dd.getStorage({
            key: 'TableInfo_count',
            success: (res) => {
              const count = Number(res && res.data) || 0;
              if (count <= 0) { resolve([]); return; }
              const gets = [];
              for (let i = 1; i <= count; i++) {
                gets.push(new Promise((resGet) => {
                  try { dd.getStorage({ key: 'TableInfo' + i, success: (r) => resGet(Array.isArray(r.data) ? r.data : (r.data || [])), fail: () => resGet([]) }); } catch (e) { resGet([]); }
                }));
              }
              Promise.all(gets).then((parts) => { try { resolve([].concat(...parts)); } catch (e) { resolve([]); } }).catch(() => resolve([]));
            },
            fail: () => {
              try { dd.getStorage({ key: 'TableInfo', success: (r) => { resolve(Array.isArray(r.data) ? r.data.slice() : (r.data || [])); }, fail: () => { resolve([]); } }); } catch (e) { resolve([]); }
            },
          });
        } catch (e) { resolve([]); }
      });
    },

    // Safe loader that falls back to legacy single-key storage when loadTableInfo isn't available
    safeLoadTableInfo() {
      if (typeof this.loadTableInfo === 'function') return this.loadTableInfo();
      return new Promise((resolve) => { try { dd.getStorage({ key: 'TableInfo', success: (r) => { resolve(Array.isArray(r.data) ? r.data.slice() : (r.data || [])); }, fail: () => { resolve([]); } }); } catch (e) { resolve([]); } });
    },

    /**
     * 获取表格信息
     */
    async getTableInfo() {
      // Call server API and accept its grouping and ranking data as-is.
      try {
        // Read SelectedTrack and isShowLookBox concurrently
        const [track, isShow] = await Promise.all([
          new Promise((resolve) => {
            try {
              dd.getStorage({ key: 'SelectedTrack', success: (res) => { resolve(res && res.data ? res.data : null); }, fail: () => { resolve(null); } });
            } catch (e) { resolve(null); }
          }),
          new Promise((resolve) => {
            try {
              dd.getStorage({ key: 'isShowLookBox', success: (res) => { resolve(!!res && !!res.data); }, fail: () => { resolve(false); } });
            } catch (e) { resolve(false); }
          })
        ]);

        // If user is a participant (isShow === true) -> pass workNumber and empty track
        // If non-participant -> pass track and empty workNumber
        const workNumberParam = isShow ? this.currentUserWorkNumber || '' : '';
        const trackParam = isShow ? '' : (track || '');

        const resp = await searchTeamAnswerInfo(this.currentUserWorkNumber && isShow ? this.currentUserWorkNumber : null, trackParam, 'PERSONAL');
        // Support both the array directly or an object with `msg` array (as seen in network preview)
        const groups = Array.isArray(resp) ? resp.slice() : (resp && Array.isArray(resp.msg) ? resp.msg.slice() : []);
        this.tableInfo = groups;
      } catch (e) {
        this.tableInfo = [];
      }
      this.buildRankLists();
      // Wait for the UI to finish rendering / images to settle before hiding the global loading overlay
      try { this.waitForRenderThenShow(); } catch (e) { this.isLoaded = true; this.clearLongLoadingTimer(); }
    },

    /**
     * 获取当前用户工号
     */
     getCurrentUserWorkNumber() {
      return new Promise((resolve) => {
      dd.getStorage({
        key: 'CurrentUserWorkNumber',
        success: (res) => {
          console.log(res, "当前用户工号");
          this.currentUserWorkNumber = res.data;
          resolve();
        },
        fail: () => {
          this.currentUserWorkNumber = '';
          resolve();
        }
      })
     })
    },
    onRegionClick(idx) {
      this.activeRegionIndex = idx;
      this.centerActiveRegion();
      this.buildRankLists();
    },
    centerActiveRegion() {
      // 尝试把活跃标签滚动到区域的中间
      const query = uni.createSelectorQuery();
      query.select('#region-' + this.activeRegionIndex).boundingClientRect();
      query.select('.region-scroll').boundingClientRect();
      // 获取当前 scroll-view 的 scrollLeft，用于计算相对位置
      query.select('.region-scroll').scrollOffset();
      query.exec((res) => {
        const tabRect = res[0];
        const scrollRect = res[1];
        const scrollOffset = res[2];
        const currentScrollLeft = (scrollOffset && scrollOffset.scrollLeft) ? scrollOffset.scrollLeft : 0;
        if (tabRect && scrollRect) {
          // 计算标签在滚动内容中的中心位置：
          // 标签相对于可视区域的 left 减去可视区域的 left 再加上当前的 scrollLeft
          const tabPositionInScroll = (tabRect.left - scrollRect.left) + currentScrollLeft;
          // 目标 scrollLeft：使当前标签居中
          const targetScrollLeft = Math.max(0, tabPositionInScroll - (scrollRect.width - tabRect.width) / 2);
          this.regionScrollLeft = targetScrollLeft;
        }
      });
    },
    navigateToPartnerRank() {
      uni.redirectTo({
        url: '/pages/teamRank/index'
      });
    },
    toNumber(v) {
      if (v === null || v === undefined) return 0;
      if (typeof v === 'number') return Number.isFinite(v) ? v : 0;
      let s = String(v).trim();
      s = s.replace(/[,\s%]/g, '');
      const n = Number(s);
      return Number.isFinite(n) ? n : 0;
    },
    buildRankLists() {
      // Use server-provided groups and ranking lists directly. Attach avatar/name/title from AllUserInfo when available.
      const groups = Array.isArray(this.tableInfo) ? this.tableInfo.slice() : [];

      // simple lookup for AllUserInfo by multiple id keys
      const findUserInfo = (id) => {
        if (!id || typeof AllUserInfo === 'undefined' || !Array.isArray(AllUserInfo)) return null;
        const s = String(id);
        try {
          return AllUserInfo.find(u => String(u.staffId) === s || String(u.workNumber) === s || String(u.staff_id) === s || String(u.id) === s) || null;
        } catch (e) { return null; }
      };

      // regionTabs come directly from groupName in server order (deduplicated)
      const tabs = [];
      const seen = new Set();
      groups.forEach(g => {
        const n = g && g.groupName ? String(g.groupName).trim() : '';
        if (n && !seen.has(n)) { seen.add(n); tabs.push(n); }
      });
      this.regionTabs = tabs;
      if (this.activeRegionIndex >= this.regionTabs.length) this.activeRegionIndex = 0;
      const selectedRegion = this.regionTabs[this.activeRegionIndex];

      // Aggregate server lists for the selected region (no '全国' special case)
      const rawItems = [];
      groups.forEach(g => { if (g && g.groupName === selectedRegion && Array.isArray(g.teamAnswerItemInfoVOList)) rawItems.push(...g.teamAnswerItemInfoVOList.map(it => Object.assign({}, it, { _groupName: g.groupName }))); });

      // enrich items with AllUserInfo lookups
      const items = rawItems.map(it => {
        const id = it.workNumber || it.staffId || it.id || it.userId || it.uid;
        const info = findUserInfo(id);
        const avatar = (it.avatarImage || it.avatar || (info && (info.avatarImage || info.avatar)) || '/static/default-avatar.jpg');
        const name = it.name || (info && (info.name || info.flowerName)) || '--';
        const title = it.title || (info && info.title) || '';
        return Object.assign({}, it, { avatarImage: avatar, name, title });
      });

      // Use server-supplied items as authoritative
      this.partnerRankList = items;

      // top three from server order by first appearance of distinct rank
      const topGroups = [];
      const seenRanks = new Set();
      for (let i = 0; i < items.length && topGroups.length < 3; i++) {
        const it = items[i];
        if (seenRanks.has(it.rank)) continue;
        seenRanks.add(it.rank);
        const members = items.filter(x => x.rank === it.rank);
        if (members.length > 1) topGroups.push({ rank: it.rank, name: '多名并列', totalScore: it.totalScore, isTieGroup: true, membersCount: members.length });
        else topGroups.push({ rank: it.rank, name: it.name, totalScore: it.totalScore, isTieGroup: false, workNumber: it.workNumber, avatarImage: it.avatarImage || '/static/default-avatar.jpg', title: it.title || '' });
      }
      this.topPartnerList = topGroups;

      // Exclude top-group ranks from the main list display so the visible rows start after the top three
      const topRanks = new Set(topGroups.map(g => g.rank));
      this.groupedPartnerList = items.filter(it => !topRanks.has(it.rank)).map(it => ({ isGroupMember: false, rank: it.rank, name: it.name, totalScore: it.totalScore, peopleCount: it.peopleCount, avgScore: it.avgScore, workNumber: it.workNumber, avatarImage: it.avatarImage || '/static/default-avatar.jpg', title: it.title || '' }));

      // my info: prefer group-level personal fields, else lookup by workNumber
      let myInfo = { rank: '-', name: '--', totalScore: 0, myContribution: 0, avatarImage: '/static/default-avatar.jpg', title: '' };
      let grp = groups.find(g => g && g.groupName === selectedRegion && (g.personalRank || g.personalName || g.personalTotalScore));
      if (!grp) grp = groups.find(g => g && (g.personalRank || g.personalName || g.personalTotalScore));
      if (grp) {
        myInfo = { rank: grp.personalRank || '-', name: grp.personalName || '--', totalScore: grp.personalTotalScore || 0, myContribution: 0, avatarImage: grp.personalAvatar || '/static/default-avatar.jpg', title: grp.personalTitle || '' };
      } else {
        const p = items.find(it => it.workNumber && String(it.workNumber) === String(this.currentUserWorkNumber));
        if (p) myInfo = { rank: p.rank, name: p.name, totalScore: p.totalScore, myContribution: 0, avatarImage: p.avatarImage || '/static/default-avatar.jpg', title: p.title || '' };
      }
      this.myPartnerInfo = myInfo;
    },
    openTieModal(score, rank = null) {
      if (score === null || score === undefined) {
        return;
      }
      this.tieTitle = `第${rank}名并列名单`;
      const tieList = this.partnerRankList.filter(p => p.totalScore === score);
      this.tieModalList = tieList.map((p) => ({
        rank: p.rank,
        name: p.name,
        totalScore: p.totalScore,
        workNumber: p.workNumber,
        avatarImage: p.avatarImage || '/static/default-avatar.jpg',
        title: p.title || ''
      }));
      this.showTieModal = true;
    },
    navigateToPersonal(workNumber) {
      uni.navigateTo({
        url: `/pages/personal/index?workNumber=${workNumber}`
      });
    },
    closeTieModal() {
      this.showTieModal = false;
      this.tieModalList = [];
      this.tieTitle = '';
    },
    startLongLoadingTimer() {
      try {
        this.longLoading = false;
        if (this.longLoadingTimer) clearTimeout(this.longLoadingTimer);
        this.longLoadingTimer = setTimeout(() => { this.longLoading = true; this.longLoadingTimer = null; }, 3000);
      } catch (e) { this.longLoading = false; this.longLoadingTimer = null; }
    },
    clearLongLoadingTimer() {
      try {
        if (this.longLoadingTimer) { clearTimeout(this.longLoadingTimer); this.longLoadingTimer = null; }
      } catch (e) {}
      this.longLoading = false;
    },
    waitForRenderThenShow() {
      if (this.renderStabilityTimer) {
        clearTimeout(this.renderStabilityTimer);
        this.renderStabilityTimer = null;
      }
      this.renderStabilityTimer = setTimeout(() => {
        this.isLoaded = true;
        this.clearLongLoadingTimer();
      }, 1000); // 1 second delay for rendering stability
    },
    clearRenderStabilityTimer() {
      if (this.renderStabilityTimer) {
        clearTimeout(this.renderStabilityTimer);
        this.renderStabilityTimer = null;
      }
    }
  }
}
</script>

<style scoped>
.container {
  min-height: 100vh;
  box-sizing: border-box;
  background: url('/static/rank-bg.png') no-repeat;
  background-size: 100%;
  background-color: #000;
  padding-bottom: 170rpx;
}
.banner-area {
  position: relative;
  height: 540rpx;
  width: 100%;
}
.btn {
  position: fixed;
  top: 438rpx;
  right: -2rpx;
  width: 302rpx;
  height: 64rpx;
  background: linear-gradient(191deg, 
    rgba(203, 192, 177, 1), 
    rgba(74, 64, 56, 1));
  display: flex;
  align-items: center;
  font-size: 28rpx;
  color: #FDEBC2;
  padding-left: 26rpx;
  border-radius: 32rpx 0rpx 0rpx 32rpx;
  z-index: 1000;
  box-sizing: border-box;
}

.btn::before {
  content: '';
  position: absolute;
  top: 2rpx;
  left: 2rpx;
  right: 2rpx;
  bottom: 2rpx;
  background: linear-gradient(180deg, #281A0F 0%, #1B0F03 100%);
  border-radius: 30rpx 0rpx 0rpx 30rpx;
  z-index: -1;
}
.region-scroll {
  height: 64rpx;
  overflow: hidden;
}
.region-tab-inner-wrapper {
  display: flex;
  flex-wrap: nowrap;
  padding: 0 20rpx;
}
.region-tab {
  display: inline-block;
  padding: 0 24rpx;
  height: 56rpx;
  line-height: 56rpx;
  margin-right: 24rpx;
  white-space: nowrap;
  color: #979797;
  font-size: 28rpx;
  background: linear-gradient( 360deg, #291A0F 0%, #231807 97%);
  border-radius: 40rpx 40rpx 40rpx 40rpx;
}
.region-tab.active {
  background: linear-gradient( 90deg, #E3CBB4 0%, #A5836C 100%);
  color: #3D2A2A;
}

.top-three-box {
  display: flex;
  justify-content: space-around;
  align-items: flex-end;
  margin: 40rpx 0;
  height: 360rpx; /* Adjusted height to accommodate the larger first place */
}

.top-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  width: 206rpx; 
  height: 278rpx;
  border-radius: 16rpx;
  box-sizing: border-box;
  position: relative;
  color: #333;
  font-size: 28rpx;
  font-weight: bold;
  padding-top: 74rpx;
  box-sizing: border-box;
}

.top-item.rank-one {
  height: 318rpx;
  background: url('/static/rank-top1-bg.png') no-repeat center center;
  background-size: 100% 100%;
  margin-bottom: 0rpx;
  padding-top: 80rpx;
}

.top-item.rank-two {
  background: url('/static/rank-top2-bg.png') no-repeat center center;
  background-size: 100% 100%;
}

.top-item.rank-three {
  background: url('/static/rank-top3-bg.png') no-repeat center center;
  background-size: 100% 100%;
}

.top-item .avatar {
  width: 80rpx;
  height: 80rpx;
  border-radius: 16rpx;
  margin-bottom: 8rpx;
  overflow: hidden;
}


.top-item .avatar-btn {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 80rpx;
  height: 80rpx;
  border-radius: 16rpx;
  margin-bottom: 8rpx;
  background-color: #999;
  overflow: hidden;
}

.top-item .name {
  font-size: 28rpx;
  font-weight: 600;
  color: #222;
  margin-bottom: 8rpx;
}

.top-item .score {
  font-size: 24rpx;
  font-weight: 400;
  color: #222;
}

.team-list-box {
  width: 702rpx;
  height: 928rpx;
  background: linear-gradient( 360deg, #241F17 0%, #352E23 97%);
  border-radius: 24rpx 24rpx 24rpx 24rpx;
  margin: 0 auto;
  overflow: hidden;
}

.list-scroll-area {
  height: 928rpx;
  padding-top: 24rpx;
}

.team-list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64rpx;
  margin-bottom: 40rpx;
  font-size: 28rpx;
  font-weight: 600;
  color: #FDEBC2;
  box-sizing: border-box;
}

.rank-column {
  width: 112rpx;
  display: flex;
  padding-left: 24rpx;
  align-items: center;
  box-sizing: border-box;
}

.rank-text {
  font-weight: bold;
}

.avatar-small {
  width: 64rpx;
  height: 64rpx;
  border-radius: 16rpx;
  margin-right: 20rpx;
  margin-left: 20rpx;
  overflow: hidden;
}

.avatar-small-btn {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 64rpx;
  height: 64rpx;
  border-radius: 16rpx;
  margin-right: 20rpx;
  margin-left: 20rpx;
  color: #333;
  background-color: #999;
  overflow: hidden;
}

.partner-name {
  flex: 2;
  text-align: left;
}

.score-value {
  flex: 1;
  text-align: center;
}

.my-partner-info {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 150rpx;
  font-size: 28rpx;
  color: #3D2A2A;
  background: linear-gradient( 90deg, #E3CBB4 0%, #A5836C 100%);
  box-sizing: border-box;
  padding: 18rpx 24rpx 0;
  z-index: 99;
}

.my-partner-info-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0 20rpx;
  box-sizing: border-box;
}

.my-rank-column {
  width: 60rpx;
  display: flex;
  justify-content: end;
  padding-right: 10rpx;
  box-sizing: border-box;
}

.my-contribution {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1.5;
}

.my-partner-btn {
  background: #d4c2a4;
  color: #333;
  padding: 8rpx 16rpx;
  border-radius: 20rpx;
  font-size: 24rpx;
  margin-bottom: 5rpx;
}

.my-contribution-text {
  font-size: 24rpx;
  color: #666;
}

.view-btn {
  margin-top: 16rpx;
  padding: 8rpx 18rpx;
  background: rgba(255,255,255,0.95);
  border-radius: 16rpx;
  font-size: 24rpx;
  color: #333;
}

/* 并列弹窗样式 */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.modal-box {
  width: 600rpx;
  background: #3E382F;
  border-radius: 24rpx;
  padding: 40rpx;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  margin-bottom: 30rpx;
}

.modal-header text {
  font-size: 36rpx;
  font-weight: 600;
  color: #FBEDD4;
  text-align: center;
}

.close-btn {
  font-size: 28rpx;
  color: #D8D8D8;
  padding: 10rpx 20rpx;
  border-radius: 30rpx;
  border: 2rpx solid #D8D8D8;
}

.modal-list {
  width: 100%;
  max-height: 600rpx; /* 控制弹窗高度 */
  overflow-y: auto;
}

.team-list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 80rpx;
  padding: 0 20rpx;
}

.team-list-item:last-child {
  border-bottom: none;
}

.rank-column {
  width: 60rpx;
  text-align: center;
  font-size: 28rpx;
  color: #D8D8D8;
}

.avatar-small {
  width: 60rpx;
  height: 60rpx;
  border-radius: 30rpx;
  margin-right: 20rpx;
  overflow: hidden;
}

.partner-name {
  flex: 1;
  font-size: 28rpx;
  color: #FBEDD4;
  margin-right: 20rpx;
}

.score-value {
  font-size: 28rpx;
  font-weight: 600;
  color: #FBEDD4;
}

/* loading overlay */
.loading-overlay {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(6,6,6,0.6);
  z-index: 3000;
}
.loading-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 30rpx;
  border-radius: 20rpx;
  background: linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0.01));
}
.spinner {
  display: flex;
  gap: 12rpx;
  margin-bottom: 18rpx;
}
.dot {
  width: 22rpx;
  height: 22rpx;
  border-radius: 50%;
  background: linear-gradient(90deg,#E3CBB4,#A5836C);
  animation: pulse 900ms infinite ease-in-out;
}
.dot:nth-child(2) { animation-delay: 120ms; }
.dot:nth-child(3) { animation-delay: 240ms; }
@keyframes pulse {
  0% { transform: scale(0.7); opacity: 0.5; }
  50% { transform: scale(1.05); opacity: 1; }
  100% { transform: scale(0.7); opacity: 0.5; }
}
.loading-text {
  color: #FDEBC2;
  font-size: 28rpx;
}
.loading-note {
  font-size: 24rpx;
  color: #FDEBC2;
  margin-bottom: 18rpx;
}
</style> 